# -*- coding: utf-8 -*-
"""
Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community
Edition) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""

import json
from django.contrib.auth.hashers import make_password
from django.db.models import Avg

from .backup import backup_table
from .models import User, OverUnderExcavationCalculation, ExcavationDiagnosis, TunnelContourInfo, DataStorage
from django.contrib.auth import authenticate, login
from django.contrib.auth import logout
from .models import DataAuditTrail
import os
from django.contrib import messages  # 用于发送消息
from .models import GeologicalSketchRecord
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
from django.db import DatabaseError
from django.http import JsonResponse
from django.shortcuts import render
from .restore import get_backup_files, BACKUP_DIR ,restore_table_from_backup

# 开发框架中通过中间件默认是需要登录态的，如有不需要登录的，可添加装饰器login_exempt
# 装饰器引入 from blueapps.account.decorators import login_exempt
def home(request):
    """
    首页
    """
    return render(request, "bk_framework_app/index_home.html")


def dev_guide(request):
    """
    开发指引
    """
    return render(request, "bk_framework_app/dev_guide.html")


def contact(request):
    """
    联系页
    """
    return render(request, "bk_framework_app/contact.html")




# 导出
from .models import  OverUnderExcavationCalculation, ExcavationDiagnosis, TunnelContourInfo, \
    GeologicalSketchRecord
import pandas as pd
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)

# #数据导出
def export_geological_view(request):
    """
    渲染掌子面导出界面，用户可以选择字段。
    """
    # 判断用户角色
    is_admin = request.user.role == 'admin' or 'auditor'

    # 可选的所有字段
    fields = [
        {'name': 'face_id', 'label': '掌子面编号'},
        {'name': 'project_id', 'label': '施工项目编号'},
        {'name': 'inspection_date', 'label': '检查日期'},
        {'name': 'distance', 'label': '里程'},
        {'name': 'design_section', 'label': '设计断面'},
        {'name': 'excavation_width', 'label': '开挖宽度'},
        {'name': 'excavation_height', 'label': '开挖高度'},
        {'name': 'excavation_area', 'label': '开挖面积'},
        {'name': 'rock_strength', 'label': '岩石强度'},
        {'name': 'weathering_degree', 'label': '风化程度'},
        {'name': 'crack_width', 'label': '裂缝宽度'},
        {'name': 'crack_shape', 'label': '裂缝形态'},
        {'name': 'water_condition', 'label': '渗水状态'},
        {'name': 'rockburst_tendency', 'label': '岩爆发育程度'},
    ]

    return render(
        request,
        'export/export_geological.html',
        {
            'fields': fields,
            'is_admin': is_admin,
        }
    )


def export_geological_records(request):
    """
    导出掌子面数据为 Excel。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 获取数据集
    if is_admin:
        records = GeologicalSketchRecord.objects.all()
    else:
        records = GeologicalSketchRecord.objects.filter(created_by=request.user)

    # 获取用户选择的字段
    default_fields = [
        'face_id', 'project_id', 'inspection_date', 'distance', 'design_section',
        'excavation_width', 'excavation_height', 'excavation_area',
        'rock_strength', 'weathering_degree', 'crack_width'
    ]
    selected_fields = request.GET.getlist('fields', default_fields) #前端传来的要导出来的数据项

    if not selected_fields:
        selected_fields = default_fields

    # 转换数据为 DataFrame
    data = pd.DataFrame.from_records(records.values(*selected_fields))

    # 处理时区问题，将所有的时间字段转换为无时区的时间
    for field in ['inspection_date', 'created_at', 'updated_at']:  # 修改为实际的时间字段名
        if field in data.columns:
            data[field] = pd.to_datetime(data[field]).dt.tz_localize(None)

    # 创建 Excel 响应
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename="geological_records.xlsx"'

    # 写入数据到 Excel
    with pd.ExcelWriter(response, engine='openpyxl') as writer:
        data.to_excel(writer, index=False, sheet_name='Geological Records')

    return response

def export_excavation_calculation_view(request):
    """
    渲染导出界面，用户可以选择超欠挖计算字段。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 可选的所有字段
    fields = [
        {'name': 'face_id', 'label': '掌子面编号'},
        {'name': 'project_id', 'label': '施工项目编号'},
        {'name': 'inspection_date', 'label': '检查日期'},
        {'name': 'measurement_date', 'label': '测量日期'},
        {'name': 'line_name', 'label': '线路名称'},
        {'name': 'north_direction_angle', 'label': '北方向角'},
        {'name': 'radius', 'label': '半径'},
        {'name': 'length', 'label': '长度'},
        {'name': 'east_coordinate', 'label': '东坐标'},
        {'name': 'north_coordinate', 'label': '北坐标'},
        {'name': 'height', 'label': '高度'},
    ]

    return render(
        request,
        'export/export_excavation_calculation.html',
        {
            'fields': fields,
            'is_admin': is_admin,
        }
    )

def export_excavation_calculation_records(request):
    """
    导出超欠挖计算数据为 Excel。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 获取数据集
    if is_admin:
        records = OverUnderExcavationCalculation.objects.all()
    else:
        records = OverUnderExcavationCalculation.objects.filter(created_by=request.user)

    # 获取用户选择的字段
    default_fields = [
        'face_id', 'project_id', 'inspection_date', 'measurement_date',
        'line_name', 'north_direction_angle', 'radius',
        'length', 'east_coordinate', 'north_coordinate', 'height'
    ]
    selected_fields = request.GET.getlist('fields', default_fields)

    if not selected_fields:
        selected_fields = default_fields

    # 转换数据为 DataFrame
    data = pd.DataFrame.from_records(records.values(*selected_fields))

    # 处理时区问题
    for field in ['inspection_date', 'measurement_date']:
        if field in data.columns:
            data[field] = pd.to_datetime(data[field]).dt.tz_localize(None)

    # 创建 Excel 响应
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename="excavation_calculation_records.xlsx"'

    # 写入数据到 Excel
    with pd.ExcelWriter(response, engine='openpyxl') as writer:
        data.to_excel(writer, index=False, sheet_name='Excavation Calculation Records')

    return response


def export_excavation_diagnosis_view(request):
    """
    渲染导出界面，用户可以选择超欠挖诊断字段。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 可选的所有字段
    fields = [
        {'name': 'face_id', 'label': '掌子面编号'},
        {'name': 'project_id', 'label': '施工项目编号'},
        {'name': 'inspection_date', 'label': '检查日期'},
        {'name': 'measurement_date', 'label': '测量日期'},
        {'name': 'mileage', 'label': '里程'},
        {'name': 'design_section', 'label': '设计断面'},
        {'name': 'measured_section', 'label': '实测断面'},
        {'name': 'over_excavation_area', 'label': '超挖面积'},
        {'name': 'under_excavation_area', 'label': '欠挖面积'},
        {'name': 'max_over_excavation', 'label': '最大超挖'},
        {'name': 'max_under_excavation', 'label': '最大欠挖'},
        {'name': 'average_over_excavation', 'label': '平均超挖'},
        {'name': 'average_under_excavation', 'label': '平均欠挖'},
    ]

    return render(
        request,
        'export/export_excavation_diagnosis.html',
        {
            'fields': fields,
            'is_admin': is_admin,
        }
    )


def export_excavation_diagnosis_records(request):
    """
    导出超欠挖诊断数据为 Excel。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 获取数据集
    if is_admin:
        records = ExcavationDiagnosis.objects.all()
    else:
        records = ExcavationDiagnosis.objects.filter(created_by=request.user)

    # 获取用户选择的字段
    default_fields = [
        'face_id', 'project_id', 'inspection_date', 'measurement_date',
        'mileage', 'design_section', 'measured_section',
        'over_excavation_area', 'under_excavation_area',
        'max_over_excavation', 'max_under_excavation',
        'average_over_excavation', 'average_under_excavation'
    ]
    selected_fields = request.GET.getlist('fields', default_fields)

    if not selected_fields:
        selected_fields = default_fields

    # 转换数据为 DataFrame
    data = pd.DataFrame.from_records(records.values(*selected_fields))

    # 处理时区问题
    for field in ['inspection_date', 'measurement_date']:
        if field in data.columns:
            data[field] = pd.to_datetime(data[field]).dt.tz_localize(None)

    # 创建 Excel 响应
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename="excavation_diagnosis_records.xlsx"'

    # 写入数据到 Excel
    with pd.ExcelWriter(response, engine='openpyxl') as writer:
        data.to_excel(writer, index=False, sheet_name='Excavation Diagnosis Records')

    return response


def export_tunnel_contour_view(request):
    """
    渲染导出界面，用户可以选择隧道轮廓字段。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 可选的所有字段
    fields = [
        {'name': 'face_id', 'label': '隧道编号'},
        {'name': 'project_id', 'label': '施工项目编号'},
        {'name': 'inspection_date', 'label': '检查日期'},
        {'name': 'measurement_date', 'label': '测量日期'},
        {'name': 'cr', 'label': '调整指数范围的常数 Cr'},
        {'name': 'w1', 'label': '权重值 w1'},
        {'name': 'w2', 'label': '权重值 w2'},
        {'name': 'w3', 'label': '权重值 w3'},
        {'name': 'od', 'label': '超挖深度 Od'},
        {'name': 'rcl', 'label': '轮廓粗糙度 RCL'},
        {'name': 'vo', 'label': '纵向超挖变化 Vo'},
        {'name': 'c1', 'label': '修正因子 c1'},
        {'name': 'c2', 'label': '修正因子 c2'},
        {'name': 'c3', 'label': '修正因子 c3'},
    ]

    return render(
        request,
        'export/export_tunnel_contour.html',
        {
            'fields': fields,
            'is_admin': is_admin,
        }
    )


def export_tunnel_contour_records(request):
    """
    导出隧道轮廓数据为 Excel。
    """
    is_admin = request.user.role == 'admin' or 'auditor'

    # 获取数据集
    if is_admin:
        records = TunnelContourInfo.objects.all()
    else:
        records = TunnelContourInfo.objects.filter(created_by=request.user)

    # 获取用户选择的字段
    default_fields = [
        'face_id', 'project_id', 'inspection_date', 'measurement_date',
        'cr', 'w1', 'w2', 'w3', 'od', 'rcl', 'vo', 'c1', 'c2', 'c3'
    ]
    selected_fields = request.GET.getlist('fields', default_fields)

    if not selected_fields:
        selected_fields = default_fields

    # 转换数据为 DataFrame
    data = pd.DataFrame.from_records(records.values(*selected_fields))

    # 处理时区问题
    for field in ['inspection_date', 'measurement_date']:
        if field in data.columns:
            data[field] = pd.to_datetime(data[field]).dt.tz_localize(None)

    # 创建 Excel 响应
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename="tunnel_contour_records.xlsx"'

    # 写入数据到 Excel
    with pd.ExcelWriter(response, engine='openpyxl') as writer:
        data.to_excel(writer, index=False, sheet_name='Tunnel Contour Records')

    return response



import json
from django.contrib.auth.hashers import make_password
from django.db.models import Avg

from .models import User, OverUnderExcavationCalculation, ExcavationDiagnosis, TunnelContourInfo
from django.contrib.auth import authenticate, login
from django.contrib.auth import logout
from .models import DataAuditTrail
import os
from django.contrib import messages  # 用于发送消息
from .models import GeologicalSketchRecord
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
from django.db import DatabaseError
from django.http import JsonResponse
from django.shortcuts import render
# 基础功能
#注册
def register_view(request):
    """
    处理用户注册请求。
    支持普通用户 (user)、管理员 (admin)、审计员 (auditor) 的角色选择。
    """
    if request.method == 'POST':  # 处理 POST 请求
        # 获取前端提交的注册表单数据
        username = request.POST.get('username')  # 用户名
        password = request.POST.get('password')  # 密码
        confirm_password = request.POST.get('confirm-password')  # 确认密码
        email = request.POST.get('email')  # 邮箱
        role = request.POST.get('role', 'user')  # 用户角色，默认为普通用户 (user)

        # 验证两次输入的密码是否一致
        if password != confirm_password:
            messages.error(request, '两次输入的密码不一致')  # 添加错误消息
            return render(request, 'base/register.html')

        # 检查用户名是否已存在
        if User.objects.filter(username=username).exists():
            messages.error(request, '用户名已存在')  # 添加错误消息
            return render(request, 'base/register.html')

        # 检查密码强度（可选，增强安全性）
        if len(password) < 6 or not any(char.isdigit() for char in password) or not any(char.isupper() for char in password) or not any(char.islower() for char in password):
            messages.error(request, '密码必须包含大小写字母、数字，且长度不少于6个字符')  # 添加错误消息
            return render(request, 'base/register.html')

        # 创建用户对象并保存到数据库
        try:
            user = User.objects.create(
                username=username,
                password=make_password(password),  # 对密码进行哈希加密
                email=email,
                role=role  # 分配角色：普通用户、管理员或审计员
            )
            user.save()  # 保存用户对象
            messages.success(request, '注册成功！请登录')  # 添加成功消息
            return redirect('login')  # 注册成功后跳转到登录页面
        except Exception as e:
            # 如果保存失败，返回错误信息到页面
            messages.error(request, f'注册失败：{str(e)}')  # 添加错误消息
            return render(request, 'base/register.html')

    # 如果是 GET 请求，渲染注册页面
    return render(request, 'base/register.html')


#登录
def login_view(request):
    """登录视图"""
    print("登录调用了")
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

        # 验证用户凭据
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            print("成功登录")

            # 强制将 next 参数设置为 index
            next_url = request.GET.get('next', '/') #这样就可以登录之后跳到index了
            print(f"Redirecting to: {next_url}")
            return redirect(next_url)
        else:
            print("登录不成功")
            return render(request, 'base/login.html', {'error': '用户名或密码错误'})

    return render(request, 'base/login.html')

from django.shortcuts import render, redirect
from django.db import connection
from django.contrib.auth import login
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.models import Session
from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import check_password
# 设置日志记录
# logger = logging.getLogger(__name__)
#
#
# from django.shortcuts import render, redirect
# from django.db import connection
# from django.contrib.auth.hashers import check_password
# from django.contrib import messages
#
# def login_view(request):
#     """用户登录视图"""
#     if request.method == 'POST':
#         username = request.POST.get('username')
#         password = request.POST.get('password')
#
#         print(f"尝试登录 - 用户名: {username}")
#
#         if not username or not password:
#             messages.error(request, '用户名或密码不能为空')
#             return render(request, 'base/login.html')
#
#         # 从自定义用户表 `bk_framework_app_user` 查询用户
#         try:
#             with connection.cursor() as cursor:
#                 cursor.execute(
#                     "SELECT id, username, password, role FROM bk_framework_app_user WHERE username = %s",
#                     [username]
#                 )
#                 user_data = cursor.fetchone()
#
#             if user_data:
#                 user_id, db_username, db_password, role = user_data
#                 print(f"数据库中存储的加密密码: {db_password}")
#                 print(f"用户输入的明文密码: {password}")
#                 print(f"用户角色: {role}")
#
#                 # 使用 Django 提供的密码验证函数
#                 if check_password(password, db_password):
#                     print("密码正确，成功登录")
#
#                     # 手动存储用户信息到 session
#                     request.session['is_authenticated'] = True
#                     request.session['user_id'] = user_id
#                     request.session['username'] = db_username
#                     request.session['role'] = role
#
#                     # 设置 session 过期时间
#                     request.session.set_expiry(3600)  # 设置1小时过期
#                     request.session.save()  # 立即保存
#
#                     print(f"Session 内容: {request.session.items()}")  # 调试输出
#
#                     messages.success(request, f'欢迎 {db_username}, 你已成功登录')
#                     return redirect('home')  # 登录成功后跳转到主页
#                 else:
#                     print("密码错误")
#                     messages.error(request, '密码错误，请重试')
#                     return render(request, 'base/login.html')
#
#             else:
#                 print("用户不存在")
#                 messages.error(request, '用户名不存在，请注册')
#                 return render(request, 'base/login.html')
#
#         except Exception as e:
#             print(f"查询用户时出错: {e}")
#             messages.error(request, '查询出错，请稍后再试')
#             return render(request, 'base/login.html')
#
#     return render(request, 'base/login.html')



#退出登录
def logout_view(request):
    """登出视图"""
    logout(request)  # 执行登出操作
    return redirect('login')  # 登出后跳转到登录页面


# 日志信息列表
def view_audit_logs(request):
    # 获取筛选条件
    user = request.GET.get('user', '')  # 默认为空字符串，表示不筛选用户
    operation_type = request.GET.get('operation_type', '')  # 筛选操作类型
    table_name = request.GET.get('table_name', '')  # 筛选操作表
    date_from = request.GET.get('date_from', '')  # 筛选起始日期
    date_to = request.GET.get('date_to', '')  # 筛选结束日期

    # 初始化查询集
    logs_list = DataAuditTrail.objects.all()

    # 应用筛选条件
    if user:
        logs_list = logs_list.filter(user__username__icontains=user)
    if operation_type:
        logs_list = logs_list.filter(operation_type=operation_type)
    if table_name:
        logs_list = logs_list.filter(table_name=table_name)
    if date_from:
        logs_list = logs_list.filter(operation_time__gte=date_from)
    if date_to:
        logs_list = logs_list.filter(operation_time__lte=date_to)

    logs_list = logs_list.order_by('-operation_time')  # 按操作时间倒序排序

    # 分页
    paginator = Paginator(logs_list, 10)  # 每页 10 条记录
    page = request.GET.get('page')
    try:
        logs = paginator.page(page)
    except PageNotAnInteger:
        logs = paginator.page(1)
    except EmptyPage:
        logs = paginator.page(paginator.num_pages)

    # 保留筛选条件的查询参数
    query_params = request.GET.copy()  # 将筛选参数复制为可变的 QueryDict
    query_params.pop('page', None)  # 移除分页参数，防止重复
    query_params_encoded = query_params.urlencode()  # 编码为 URL 查询字符串

    # 传递筛选条件和分页数据
    return render(request, 'base/view_audit_logs.html', {
        'logs': logs,
        'filters': {
            'user': user,
            'operation_type': operation_type,
            'table_name': table_name,
            'date_from': date_from,
            'date_to': date_to,
        },
        'operation_types': DataAuditTrail.objects.values_list('operation_type', flat=True).distinct(),
        'table_names': DataAuditTrail.objects.values_list('table_name', flat=True).distinct(),
        'query_params': query_params_encoded,  # 将筛选参数编码并传递给模板
    })


# 详细日志信息
def view_log_detail(request, log_id):
    # 获取指定日志记录
    log = get_object_or_404(DataAuditTrail, id=log_id)

    # 解析 JSON 数据
    try:
        data_snapshot = json.loads(log.data_snapshot) if log.data_snapshot else None
        updated_data = json.loads(log.updated_data) if log.updated_data else None
        change_details = json.loads(log.change_details) if log.change_details else None
    except json.JSONDecodeError:
        data_snapshot = updated_data = change_details = None  # 防止解析失败

    return render(request, 'base/log_detail.html', {
        'log': log,
        'data_snapshot': data_snapshot,
        'updated_data': updated_data,
        'change_details': change_details,
    })

@login_required
def backup_view(request):
    """
    备份视图
    - GET 请求：渲染备份页面
    - POST 请求：触发备份操作（支持备份单个表或多个表）
    """
    if request.method == 'POST':
        try:
            body = json.loads(request.body)  # 解析 JSON 数据
            table_names = body.get("table_names", [])  # 获取表名称列表

            if not table_names:
                return JsonResponse({"status": "error", "message": "未选择任何表进行备份"})

            supported_tables = {
                "GeologicalSketchRecord": GeologicalSketchRecord.objects.all(),
                "OverUnderExcavationCalculation": OverUnderExcavationCalculation.objects.all(),
                "ExcavationDiagnosis": ExcavationDiagnosis.objects.all(),
                "TunnelContourInfo": TunnelContourInfo.objects.all(),
                "DataStorage": DataStorage.objects.all(),  # 新增 DataStorage 表
            }

            backup_results = []
            success_count = 0

            for table_name in table_names:
                if table_name in supported_tables:
                    queryset = supported_tables[table_name]
                    try:
                        logger.info(f"开始备份表：{table_name}")
                        result = backup_table(queryset, table_name)
                        backup_results.append(result)

                        if result.get("status") == "success":
                            success_count += 1
                    except Exception as table_error:
                        logger.error(f"备份表 {table_name} 失败：{table_error}")
                        backup_results.append({"status": "error", "message": f"表 {table_name} 备份失败：{str(table_error)}"})
                else:
                    backup_results.append({"status": "error", "message": f"表 {table_name} 不存在"})

            overall_status = "success" if success_count == len(table_names) else "partial_success"

            return JsonResponse({
                "status": overall_status,
                "message": "备份完成" if overall_status == "success" else "部分表备份失败",
                "results": backup_results
            })

        except json.JSONDecodeError:
            logger.error("请求数据格式错误，无法解析 JSON")
            return JsonResponse({"status": "error", "message": "请求数据格式错误，无法解析 JSON"})
        except DatabaseError as db_error:
            logger.error(f"数据库错误：{db_error}")
            return JsonResponse({"status": "error", "message": f"数据库错误：{str(db_error)}"})
        except Exception as e:
            logger.error(f"备份失败：{e}")
            return JsonResponse({"status": "error", "message": f"备份失败：{str(e)}"})

    else:
        return render(request, 'base/backup.html')



# 下面这些是数据库恢复的
def restore_database(request):
    """
    恢复数据库的视图
    :param request: 请求对象
    :return: 恢复结果的 JSON 或 HTML 页面
    """
    if request.method == "POST":
        # 获取前端提交的备份文件名
        file_name = request.POST.get("backup_file")

        if not file_name:
            return JsonResponse({"status": "error", "message": "未选择备份文件"})

        # 获取文件路径
        file_path = os.path.join(BACKUP_DIR, file_name)
        if not os.path.exists(file_path):
            return JsonResponse({"status": "error", "message": "备份文件不存在"})

        # 匹配目标表模型：通过文件名推断目标表
        #这个是将数据库恢复到正常的四个表里面的（tunnelcontourinfo等等）
        table_mapping = {
            "GeologicalSketchRecord": GeologicalSketchRecord,
            "OverUnderExcavationCalculation": OverUnderExcavationCalculation,
            "ExcavationDiagnosis": ExcavationDiagnosis,
            "TunnelContourInfo": TunnelContourInfo,  #这个如果删除Backup就是恢复原来的数据库了（就是把TunnelContourInfo表清空然后根据json重新恢复）
            "DataStorage": DataStorage,  # 新增的表
        }

        # 下面的这个是将数据库恢复到四个备份表里面的（tunnelcontourinfobackup等等）
        # table_mapping = {
        #     "GeologicalSketchRecord": GeologicalSketchRecordBackup,
        #     "OverUnderExcavationCalculation": OverUnderExcavationCalculationBackup,
        #     "ExcavationDiagnosis": ExcavationDiagnosisBackup,
        #     "TunnelContourInfo": TunnelContourInfoBackup,  #这个如果删除Backup就是恢复原来的数据库了（就是把TunnelContourInfo表清空然后根据json重新恢复）
        # }

        # 提取表名，例如从文件名中解析出 "GeologicalSketchRecordBackup"
        table_name = None
        for key in table_mapping.keys():
            if key in file_name:
                table_name = key
                break

        print(table_name)
        if not table_name:
            return JsonResponse({"status": "error", "message": "无法识别目标表，请检查备份文件名"})

        model_class = table_mapping.get(table_name)

        # 调用单个表恢复函数
        result = restore_table_from_backup(file_path, model_class)
        return JsonResponse(result)

    else:
        # GET 请求，显示可用备份文件
        backup_files = get_backup_files()
        return render(
            request,
            "base/restore_database.html",
            {"backup_files": backup_files},
        )

#清空session的
@login_required
def clear_message(request):
    """清除 session 消息"""
    request.session.pop('message', None)
    request.session.pop('message_type', None)
    return JsonResponse({'status': 'success'})


# 主界面数据展示
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from .models import (
    GeologicalSketchRecord,
    ExcavationDiagnosis,
    OverUnderExcavationCalculation,
    TunnelContourInfo,
)

from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from .models import GeologicalSketchRecord, ExcavationDiagnosis, OverUnderExcavationCalculation, TunnelContourInfo

@login_required
def get_status_counts(request):
    """
    返回当前用户每种审批状态的记录数量。
    """
    status_labels = {
        'pending': '新增待审批',
        'modified_pending': '修改待审批',
        'deleted_pending': '删除待审批',
        'rejected': '未通过审批',
        'uploaded_approved': '有效数据'
    }

    # 初始化计数
    status_counts = {label: 0 for label in status_labels.values()}

    # 累计各状态的记录数量
    for model in [GeologicalSketchRecord, ExcavationDiagnosis, OverUnderExcavationCalculation, TunnelContourInfo]:
        status_counts['新增待审批'] += model.objects.filter(created_by=request.user, status='pending').count()
        status_counts['修改待审批'] += model.objects.filter(created_by=request.user, status='modified_pending').count()
        status_counts['删除待审批'] += model.objects.filter(created_by=request.user, status='deleted_pending').count()
        status_counts['未通过审批'] += model.objects.filter(created_by=request.user, status='rejected').count()
        status_counts['有效数据'] += model.objects.filter(created_by=request.user, status='uploaded_approved').count()

    print(status_counts)
    return JsonResponse({'data': status_counts})


@login_required
def geological_statistics_data(request):
    """
    返回掌子面统计数据，用于主界面图表展示。
    """
    # 查询用户相关数据
    records = GeologicalSketchRecord.objects.filter(created_by=request.user)
    if not records.exists():
        return JsonResponse({'data': [0, 0, 0, 0]})  # 如果没有数据，返回全 0

    excavation_width_avg = records.aggregate(Avg('excavation_width'))['excavation_width__avg'] or 0
    excavation_height_avg = records.aggregate(Avg('excavation_height'))['excavation_height__avg'] or 0
    excavation_area_avg = records.aggregate(Avg('excavation_area'))['excavation_area__avg'] or 0
    rock_strength_avg = records.aggregate(Avg('rock_strength'))['rock_strength__avg'] or 0

    return JsonResponse({
        'data': [
            excavation_width_avg,
            excavation_height_avg,
            excavation_area_avg,
            rock_strength_avg,
        ]
    })


from .forms import GeologicalSketchRecordForm, ExcavationDiagnosisForm, OverUnderExcavationForm, TunnelContourForm
from django.db import transaction
from .check import (
    check_geological_data_validity,
    check_excavation_diagnosis_validity,
    check_excavation_calculation_validity,
    check_tunnel_data_validity,
)
from django.shortcuts import render, redirect, get_object_or_404
import pandas as pd
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
from django.http import JsonResponse
from django.shortcuts import render


#掌子面信息上传（管理员）（支持excel导入版本）
@login_required
def upload_geological_info(request):
    """
    管理员上传地质信息视图（支持单条记录提交和 Excel 批量导入）。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")  # 调试输出提交类型

        if submit_type == 'single':  # 单条记录提交
            print("开始单条记录提交处理")
            form = GeologicalSketchRecordForm(request.POST)
            if form.is_valid():
                print("单条记录验证通过，准备保存")
                geological_record = form.save(commit=False)  # 保存表单数据到实例，但不提交到数据库
                geological_record.created_by = request.user  # 设置当前用户为创建者
                geological_record.status = 'uploaded_approved'  # 设置状态为已审批
                geological_record.save()  # 保存数据到数据库
                print("单条记录保存成功")
                return redirect('geological_list')  # 成功后重定向到地质记录列表页面
            else:
                print("单条记录表单验证失败")
                print(f"表单错误: {form.errors}")  # 调试输出表单错误
                return render(
                    request,
                    'upload/upload_geological_info.html',
                    {'form': form, 'errors': form.errors}
                )

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                print("开始批量导入处理")
                excel_file = request.FILES['excel_file']
                try:
                    # 读取 Excel 文件，指定 openpyxl 引擎
                    data = pd.read_excel(excel_file, engine='openpyxl')
                    print(f"读取到的 Excel 数据:\n{data.head()}")  # 调试输出 Excel 文件的前几行

                    # 检查 Excel 数据的必填字段
                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'distance',
                        'design_section', 'inspector', 'measurement_date', 'excavation_width',
                        'excavation_height', 'excavation_area', 'excavation_method',
                        'face_condition', 'excavation_condition', 'rock_strength',
                        'weathering_degree', 'crack_width', 'crack_shape', 'water_condition',
                        'rockburst_tendency', 'rock_grade', 'karst_development', 'water_status'
                    ]

                    # 检查缺少的字段
                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        return JsonResponse({
                            "status": "error",
                            "message": f"批量导入失败: 缺少必要字段 {', '.join(missing_columns)}"
                        })

                    # 强制转换字段类型
                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = ['design_section', 'distance', 'excavation_width', 'excavation_height', 'excavation_area',
                                       'rock_strength', 'crack_width']
                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')  # 转换为日期字符串
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce')  # 非数值将被置为 NaN
                            if data[col].isnull().any():  # 如果存在 NaN 值
                                print(f"列 {col} 包含无法转换为数字的值。将替换为 0。")
                                print(f"问题值:\n{data[data[col].isnull()][col]}")  # 输出问题值
                                data[col] = data[col].fillna(0)  # 替换 NaN 为默认值 0

                    # 保存每一行数据
                    errors = []
                    success_count = 0
                    with transaction.atomic():  # 确保事务一致性
                        for index, row in data.iterrows():
                            print(f"正在处理第 {index + 1} 行数据: {row.to_dict()}")  # 调试输出当前行数据
                            form_data = row.to_dict()  # 转换为字典格式
                            form = GeologicalSketchRecordForm(form_data)

                            if form.is_valid():
                                geological_record = form.save(commit=False)
                                geological_record.created_by = request.user
                                geological_record.status = 'uploaded_approved'
                                geological_record.save()
                                success_count += 1
                                print(f"第 {index + 1} 行数据保存成功")
                            else:
                                print(f"第 {index + 1} 行数据验证失败: {form.errors}")  # 调试输出当前行错误
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    if errors:
                        print(f"部分数据导入失败，共成功导入 {success_count} 条记录")
                        return JsonResponse({
                            "status": "partial_success",
                            "message": f"批量导入完成，但有部分错误。",
                            "success_count": success_count,
                            "errors": errors
                        })

                    print(f"批量导入成功，共导入 {success_count} 条记录")
                    return redirect('geological_list')  # 成功后重定向到地质记录列表页面

                except Exception as e:
                    print(f"批量导入失败: {str(e)}")  # 调试输出异常信息
                    return JsonResponse({"status": "error", "message": f"批量导入失败: {str(e)}"})

            print("未上传 Excel 文件")
            return JsonResponse({"status": "error", "message": "未上传 Excel 文件。"})

        else:
            print("无效的提交类型")
            return JsonResponse({"status": "error", "message": "无效的提交类型。"})

    else:  # GET 请求
        print("GET 请求，渲染空表单")
        form = GeologicalSketchRecordForm()
        return render(request, 'upload/upload_geological_info.html', {'form': form})


## 掌子面信息上传（用户）（支持批量导入）
@login_required
def upload_geological_info_user(request):
    """
    用户上传地质信息视图。
    - 支持单条记录和 Excel 批量导入。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")  # 调试输出提交类型

        if submit_type == 'single':  # 单条记录提交
            form = GeologicalSketchRecordForm(request.POST)
            if form.is_valid():
                geological_record = form.save(commit=False)
                geological_record.created_by = request.user
                geological_record.operation_reason = request.POST.get('operation_reason')

                is_valid, validation_errors = check_geological_data_validity(geological_record)

                if is_valid:
                    geological_record.status = 'uploaded_approved'
                else:
                    geological_record.status = 'pending'

                geological_record.save()
                if not is_valid:
                    request.session['message'] = f"单条提交成功，但部分数据未达标: {', '.join(validation_errors)}"
                    request.session['message_type'] = "warning"

                return redirect('user_records_view')  # 跳转到个人信息中心

            else:
                request.session['message'] = "单条提交失败，请检查数据格式。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')

                    # 必填字段校验
                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'distance',
                        'design_section', 'inspector', 'measurement_date', 'excavation_width',
                        'excavation_height', 'excavation_area', 'excavation_method',
                        'face_condition', 'excavation_condition', 'rock_strength',
                        'weathering_degree', 'crack_width', 'crack_shape', 'water_condition',
                        'rockburst_tendency', 'rock_grade', 'karst_development', 'water_status', 'operation_reason'
                    ]

                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        request.session['message'] = f"批量导入失败: 缺少必要字段 {', '.join(missing_columns)}"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    # 数据类型转换
                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = ['distance', 'design_section', 'excavation_width', 'excavation_height', 'excavation_area',
                                       'rock_strength', 'crack_width']
                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    # 保存记录
                    errors = []
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = GeologicalSketchRecordForm(form_data)

                            if form.is_valid():
                                geological_record = form.save(commit=False)
                                geological_record.created_by = request.user

                                is_valid, _ = check_geological_data_validity(geological_record)

                                if is_valid:
                                    geological_record.status = 'uploaded_approved'
                                else:
                                    geological_record.status = 'pending'

                                geological_record.save()
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    # 判断批量导入结果
                    if errors:
                        request.session['message'] = f"批量导入部分失败，共 {len(errors)} 条记录有问题。"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    return redirect('user_records_view')  # 全部成功直接跳转

                except Exception as e:
                    request.session['message'] = f"批量导入失败: {str(e)}"
                    request.session['message_type'] = "error"
                    return redirect('user_records_view')

            else:
                request.session['message'] = "未上传 Excel 文件，无法进行批量导入。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

    else:
        form = GeologicalSketchRecordForm()
    return render(request, 'upload/upload_geological_info_user.html', {'form': form})


## 超欠挖计算信息上传（用户，支持批量导入）
@login_required
def upload_excavation_calculation_user(request):
    """
    用户上传超欠挖计算信息视图。
    - 支持单条记录和 Excel 批量导入。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")  # 调试输出提交类型

        if submit_type == 'single':  # 单条记录提交
            form = OverUnderExcavationForm(request.POST)
            if form.is_valid():
                calculation_record = form.save(commit=False)
                calculation_record.created_by = request.user
                calculation_record.operation_reason = request.POST.get('operation_reason')

                is_valid, validation_errors = check_excavation_diagnosis_validity(calculation_record)

                if is_valid:
                    calculation_record.status = 'uploaded_approved'
                else:
                    calculation_record.status = 'pending'

                calculation_record.save()
                if not is_valid:
                    request.session['message'] = f"单条提交成功，但部分数据未达标: {', '.join(validation_errors)}"
                    request.session['message_type'] = "warning"

                return redirect('user_records_view')  # 跳转到个人信息中心

            else:
                request.session['message'] = "单条提交失败，请检查数据格式。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')

                    # 必填字段校验
                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'measurement_date',
                        'line_name', 'north_direction_angle', 'radius', 'length',
                        'east_coordinate', 'north_coordinate', 'start_offset', 'height',
                        'radius_section', 'angle_increment', 'operation_reason'
                    ]

                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        request.session['message'] = f"批量导入失败: 缺少必要字段 {', '.join(missing_columns)}"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    # 数据类型转换
                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = [
                        'north_direction_angle', 'radius', 'length', 'east_coordinate',
                        'north_coordinate', 'start_offset', 'height', 'radius_section', 'angle_increment'
                    ]
                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    # 保存记录
                    errors = []
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = OverUnderExcavationForm(form_data)

                            if form.is_valid():
                                calculation_record = form.save(commit=False)
                                calculation_record.created_by = request.user

                                is_valid, _ = check_excavation_diagnosis_validity(calculation_record)

                                if is_valid:
                                    calculation_record.status = 'uploaded_approved'
                                else:
                                    calculation_record.status = 'pending'

                                calculation_record.save()
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    # 判断批量导入结果
                    if errors:
                        request.session['message'] = f"批量导入部分失败，共 {len(errors)} 条记录有问题。"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    return redirect('user_records_view')  # 全部成功直接跳转

                except Exception as e:
                    request.session['message'] = f"批量导入失败: {str(e)}"
                    request.session['message_type'] = "error"
                    return redirect('user_records_view')

            else:
                request.session['message'] = "未上传 Excel 文件，无法进行批量导入。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

    else:
        form = OverUnderExcavationForm()
    return render(request, 'upload/upload_info3_user.html', {'form': form})

## 超欠挖诊断信息上传（用户，支持批量导入）
@login_required
def upload_excavation_diagnosis_user(request):
    """
    用户上传超欠挖诊断信息视图。
    - 支持单条记录和 Excel 批量导入。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")  # 调试输出提交类型

        if submit_type == 'single':  # 单条记录提交
            form = ExcavationDiagnosisForm(request.POST)
            if form.is_valid():
                diagnosis_record = form.save(commit=False)
                diagnosis_record.created_by = request.user
                diagnosis_record.operation_reason = request.POST.get('operation_reason')

                is_valid, validation_errors = check_excavation_calculation_validity(diagnosis_record)

                if is_valid:
                    diagnosis_record.status = 'uploaded_approved'
                else:
                    diagnosis_record.status = 'pending'

                diagnosis_record.save()
                if not is_valid:
                    request.session['message'] = f"单条提交成功，但部分数据未达标: {', '.join(validation_errors)}"
                    request.session['message_type'] = "warning"

                return redirect('user_records_view')  # 跳转到个人信息中心

            else:
                request.session['message'] = "单条提交失败，请检查数据格式。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')

                    # 必填字段校验
                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'measurement_date',
                        'scale', 'mileage', 'design_section', 'line_x', 'line_y',
                        'measured_section', 'reference_section', 'line_height', 'over_excavation_area',
                        'under_excavation_area', 'max_over_excavation', 'max_under_excavation',
                        'average_over_excavation', 'average_under_excavation', 'diagnosis_result', 'inspector', 'operation_reason'
                    ]

                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        request.session['message'] = f"批量导入失败: 缺少必要字段 {', '.join(missing_columns)}"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    # 数据类型转换
                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = [
                        'scale', 'mileage', 'design_section', 'line_x', 'line_y',
                        'measured_section', 'reference_section', 'line_height',
                        'over_excavation_area', 'under_excavation_area',
                        'max_over_excavation', 'max_under_excavation',
                        'average_over_excavation', 'average_under_excavation'
                    ]
                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    # 保存记录
                    errors = []
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = ExcavationDiagnosisForm(form_data)

                            if form.is_valid():
                                diagnosis_record = form.save(commit=False)
                                diagnosis_record.created_by = request.user

                                is_valid, _ = check_excavation_calculation_validity(diagnosis_record)

                                if is_valid:
                                    diagnosis_record.status = 'uploaded_approved'
                                else:
                                    diagnosis_record.status = 'pending'

                                diagnosis_record.save()
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    # 判断批量导入结果
                    if errors:
                        request.session['message'] = f"批量导入部分失败，共 {len(errors)} 条记录有问题。"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    return redirect('user_records_view')  # 全部成功直接跳转

                except Exception as e:
                    request.session['message'] = f"批量导入失败: {str(e)}"
                    request.session['message_type'] = "error"
                    return redirect('user_records_view')

            else:
                request.session['message'] = "未上传 Excel 文件，无法进行批量导入。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

    else:
        form = ExcavationDiagnosisForm()
    return render(request, 'upload/upload_info2_user.html', {'form': form})

## 隧道信息上传（用户，支持批量导入）
@login_required
def upload_tunnel_info_user(request):
    """
    用户上传隧道信息视图。
    - 支持单条记录和 Excel 批量导入。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")  # 调试输出提交类型

        if submit_type == 'single':  # 单条记录提交
            form = TunnelContourForm(request.POST)
            if form.is_valid():
                tunnel_info = form.save(commit=False)
                tunnel_info.created_by = request.user
                tunnel_info.operation_reason = request.POST.get('operation_reason')

                is_valid, validation_errors = check_tunnel_data_validity(tunnel_info)

                if is_valid:
                    tunnel_info.status = 'uploaded_approved'
                else:
                    tunnel_info.status = 'pending'

                tunnel_info.save()
                if not is_valid:
                    request.session['message'] = f"单条提交成功，但部分数据未达标: {', '.join(validation_errors)}"
                    request.session['message_type'] = "warning"

                return redirect('user_records_view')  # 跳转到个人信息中心

            else:
                request.session['message'] = "单条提交失败，请检查数据格式。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')

                    # 必填字段校验
                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'measurement_date',
                        'inspector', 'od', 'rcl', 'vo', 'cr',
                        'w1', 'w2', 'w3', 'c1', 'c2', 'c3', 'operation_reason'
                    ]

                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        request.session['message'] = f"批量导入失败: 缺少必要字段 {', '.join(missing_columns)}"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    # 数据类型转换
                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = ['od', 'rcl', 'vo', 'cr', 'w1', 'w2', 'w3', 'c1', 'c2', 'c3']
                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    # 保存记录
                    errors = []
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = TunnelContourForm(form_data)

                            if form.is_valid():
                                tunnel_info = form.save(commit=False)
                                tunnel_info.created_by = request.user

                                is_valid, _ = check_tunnel_data_validity(tunnel_info)

                                if is_valid:
                                    tunnel_info.status = 'uploaded_approved'
                                else:
                                    tunnel_info.status = 'pending'

                                tunnel_info.save()
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    # 判断批量导入结果
                    if errors:
                        request.session['message'] = f"批量导入部分失败，共 {len(errors)} 条记录有问题。"
                        request.session['message_type'] = "error"
                        return redirect('user_records_view')

                    return redirect('user_records_view')  # 全部成功直接跳转

                except Exception as e:
                    request.session['message'] = f"批量导入失败: {str(e)}"
                    request.session['message_type'] = "error"
                    return redirect('user_records_view')

            else:
                request.session['message'] = "未上传 Excel 文件，无法进行批量导入。"
                request.session['message_type'] = "error"
                return redirect('user_records_view')

    else:
        form = TunnelContourForm()
    return render(request, 'upload/upload_info1_user.html', {'form': form})

# 超欠挖诊断信息上传（管理员）（支持excel导入版本）
@login_required
def upload_info2(request):
    """
    管理员上传超欠挖诊断信息视图（支持单条记录提交和 Excel 批量导入）。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')  # 获取提交类型
        print(f"提交类型: {submit_type}")

        if submit_type == 'single':  # 单条记录提交
            form = ExcavationDiagnosisForm(request.POST)
            if form.is_valid():
                diagnosis_record = form.save(commit=False)
                diagnosis_record.created_by = request.user
                diagnosis_record.status = 'uploaded_approved'
                diagnosis_record.save()
                print("单条记录保存成功")
                return redirect('excavation_diagnosis_list')
            else:
                print("单条记录表单验证失败")
                return render(request, 'upload/upload_info2.html', {'form': form, 'errors': form.errors})

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')
                    required_columns = [
                        'face_id', 'project_id', 'measurement_date', 'inspection_date',
                        'scale', 'mileage', 'design_section', 'line_x', 'line_y',
                        'measured_section', 'reference_section', 'line_height',
                        'over_excavation_area', 'under_excavation_area',
                        'max_over_excavation', 'max_under_excavation',
                        'average_over_excavation', 'average_under_excavation',
                        'diagnosis_result', 'inspector'
                    ]
                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        return JsonResponse({"status": "error", "message": f"缺少必要字段 {', '.join(missing_columns)}"})

                    date_columns = ['measurement_date', 'inspection_date']
                    numeric_columns = ['scale', 'mileage', 'design_section',
                                       'line_x', 'line_y', 'measured_section',
                                       'reference_section', 'line_height',
                                       'over_excavation_area', 'under_excavation_area',
                                       'max_over_excavation', 'max_under_excavation',
                                       'average_over_excavation', 'average_under_excavation']

                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    errors = []
                    success_count = 0
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form = ExcavationDiagnosisForm(row.to_dict())
                            if form.is_valid():
                                diagnosis_record = form.save(commit=False)
                                diagnosis_record.created_by = request.user
                                diagnosis_record.status = 'uploaded_approved'
                                diagnosis_record.save()
                                success_count += 1
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    if errors:
                        return JsonResponse({
                            "status": "partial_success",
                            "message": f"批量导入完成，但有部分错误。",
                            "success_count": success_count,
                            "errors": errors
                        })

                    return redirect('excavation_diagnosis_list')

                except Exception as e:
                    return JsonResponse({"status": "error", "message": f"批量导入失败: {str(e)}"})

            return JsonResponse({"status": "error", "message": "未上传 Excel 文件。"})

        else:
            return JsonResponse({"status": "error", "message": "无效的提交类型。"})

    else:
        form = ExcavationDiagnosisForm()
        return render(request, 'upload/upload_info2.html', {'form': form})


# 超欠挖计算信息上传（管理员）（支持excel导入版本）
@login_required
def upload_info3(request):
    """
    管理员上传超欠挖计算信息视图（支持单条记录提交和 Excel 批量导入）。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')
        print(f"提交类型: {submit_type}")

        if submit_type == 'single':  # 单条记录提交
            form = OverUnderExcavationForm(request.POST)
            if form.is_valid():
                calculation_record = form.save(commit=False)
                calculation_record.created_by = request.user
                calculation_record.status = 'uploaded_approved'
                calculation_record.save()
                print("单条记录保存成功")
                return redirect('excavation_calculation_list')
            else:
                print("单条记录表单验证失败")
                return render(request, 'upload/upload_info3.html', {'form': form, 'errors': form.errors})

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')
                    print(f"读取到的 Excel 数据:\n{data.head()}")

                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'measurement_date',
                        'inspector', 'line_name', 'north_direction_angle', 'radius',
                        'length', 'east_coordinate', 'north_coordinate', 'start_offset',
                        'height', 'radius_section', 'angle_increment'
                    ]
                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        return JsonResponse({"status": "error", "message": f"缺少必要字段 {', '.join(missing_columns)}"})

                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = [
                        'north_direction_angle', 'radius', 'length', 'east_coordinate',
                        'north_coordinate', 'start_offset', 'height', 'radius_section',
                        'angle_increment'
                    ]

                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    errors = []
                    success_count = 0
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = OverUnderExcavationForm(form_data)
                            if form.is_valid():
                                calculation_record = form.save(commit=False)
                                calculation_record.created_by = request.user
                                calculation_record.status = 'uploaded_approved'
                                calculation_record.save()
                                success_count += 1
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    if errors:
                        return JsonResponse({
                            "status": "partial_success",
                            "message": f"批量导入完成，但有部分错误。",
                            "success_count": success_count,
                            "errors": errors
                        })

                    print(f"批量导入成功，共导入 {success_count} 条记录")
                    return redirect('excavation_calculation_list')

                except Exception as e:
                    print(f"批量导入失败: {str(e)}")
                    return JsonResponse({"status": "error", "message": f"批量导入失败: {str(e)}"})

            return JsonResponse({"status": "error", "message": "未上传 Excel 文件。"})

        else:
            return JsonResponse({"status": "error", "message": "无效的提交类型。"})

    else:
        form = OverUnderExcavationForm()
        return render(request, 'upload/upload_info3.html', {'form': form})


# 隧道轮廓信息上传（管理员）（支持excel导入版本）
@login_required
def upload_info1(request):
    """
    管理员上传隧道轮廓信息视图（支持单条记录提交和 Excel 批量导入）。
    """
    if request.method == 'POST':
        submit_type = request.POST.get('submit_type', 'single')
        print(f"提交类型: {submit_type}")

        if submit_type == 'single':  # 单条记录提交
            form = TunnelContourForm(request.POST)
            if form.is_valid():
                profile_record = form.save(commit=False)
                profile_record.created_by = request.user
                profile_record.status = 'uploaded_approved'
                profile_record.save()
                print("单条记录保存成功")
                return redirect('tunnel_contour_info_list')
            else:
                print("单条记录表单验证失败")
                return render(request, 'upload/upload_info1.html', {'form': form, 'errors': form.errors})

        elif submit_type == 'bulk':  # 批量导入
            if 'excel_file' in request.FILES:
                excel_file = request.FILES['excel_file']
                try:
                    data = pd.read_excel(excel_file, engine='openpyxl')
                    print(f"读取到的 Excel 数据:\n{data.head()}")

                    required_columns = [
                        'face_id', 'project_id', 'inspection_date', 'measurement_date',
                        'inspector', 'od', 'rcl', 'vo', 'cr', 'w1', 'w2', 'w3', 'c1', 'c2', 'c3'
                    ]
                    missing_columns = [col for col in required_columns if col not in data.columns]
                    if missing_columns:
                        return JsonResponse({"status": "error", "message": f"缺少必要字段 {', '.join(missing_columns)}"})

                    date_columns = ['inspection_date', 'measurement_date']
                    numeric_columns = ['od', 'rcl', 'vo', 'cr', 'w1', 'w2', 'w3', 'c1', 'c2', 'c3']

                    for col in date_columns:
                        if col in data.columns:
                            data[col] = pd.to_datetime(data[col], errors='coerce').dt.strftime('%Y-%m-%d')
                    for col in numeric_columns:
                        if col in data.columns:
                            data[col] = pd.to_numeric(data[col], errors='coerce').fillna(0)

                    errors = []
                    success_count = 0
                    with transaction.atomic():
                        for index, row in data.iterrows():
                            form_data = row.to_dict()
                            form = TunnelContourForm(form_data)
                            if form.is_valid():
                                profile_record = form.save(commit=False)
                                profile_record.created_by = request.user
                                profile_record.status = 'uploaded_approved'
                                profile_record.save()
                                success_count += 1
                            else:
                                errors.append({"row": index + 1, "errors": form.errors.as_json()})

                    if errors:
                        return JsonResponse({
                            "status": "partial_success",
                            "message": f"批量导入完成，但有部分错误。",
                            "success_count": success_count,
                            "errors": errors
                        })

                    print(f"批量导入成功，共导入 {success_count} 条记录")
                    return redirect('tunnel_contour_info_list')

                except Exception as e:
                    print(f"批量导入失败: {str(e)}")
                    return JsonResponse({"status": "error", "message": f"批量导入失败: {str(e)}"})

            return JsonResponse({"status": "error", "message": "未上传 Excel 文件。"})

        else:
            return JsonResponse({"status": "error", "message": "无效的提交类型。"})

    else:
        form = TunnelContourForm()
        return render(request, 'upload/upload_info1.html', {'form': form})



import json
import matplotlib.pyplot as plt
import io
import base64
import pandas as pd
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
from django.shortcuts import render
from .models import GeologicalSketchRecord,OverUnderExcavationCalculation,ExcavationDiagnosis,TunnelContourInfo


#下面四个是统计信息
@login_required
def geological_statistics(request):
    """
    Geological statistics view:
    - Users see their own data statistics.
    - Admins see global data statistics.
    """

    # Determine if the user is an admin
    is_admin = request.user.role == 'admin'

    # Get the dataset: Admins get all data, regular users get only their own data
    if is_admin:
        records = GeologicalSketchRecord.objects.all()
    else:
        records = GeologicalSketchRecord.objects.filter(created_by=request.user)

    # Prepare the dataset
    data = pd.DataFrame.from_records(
        records.values(
            'excavation_width',
            'excavation_height',
            'excavation_area',
            'design_section',
            'rock_strength',
            'weathering_degree',
            'created_at',  # Record creation time for trend analysis
        )
    )

    # Initialize statistics and charts
    statistics = {}
    charts = {}

    if not data.empty:
        # Convert relevant columns to numeric and clean data
        for col in ['excavation_width', 'excavation_height', 'excavation_area', 'rock_strength']:
            data[col] = pd.to_numeric(data[col], errors='coerce')  # Convert to numeric, invalid entries become NaN

        # Drop rows with NaN in critical numeric columns
        data = data.dropna(subset=['excavation_width', 'excavation_height', 'excavation_area', 'rock_strength'])

        # Compute statistics
        statistics['record_count'] = len(data)  # Number of records uploaded
        statistics['excavation_width_avg'] = data['excavation_width'].mean()  # Average excavation width
        statistics['excavation_height_avg'] = data['excavation_height'].mean()  # Average excavation height
        statistics['excavation_area_avg'] = data['excavation_area'].mean()  # Average excavation area
        statistics['rock_strength_avg'] = data['rock_strength'].mean()  # Average rock strength

        # Ensure 'created_at' is in datetime format
        if 'created_at' in data:
            data['created_at'] = pd.to_datetime(data['created_at'], errors='coerce')  # Convert to datetime
            data = data.dropna(subset=['created_at'])  # Remove rows with invalid dates

        # Generate excavation width distribution histogram
        try:
            plt.figure(figsize=(8, 4))
            data['excavation_width'].plot(kind='hist', bins=10, color='blue', alpha=0.7, edgecolor='black')
            plt.title('Excavation Width Distribution')
            plt.xlabel('Excavation Width')
            plt.ylabel('Frequency')
            buf = io.BytesIO()
            plt.savefig(buf, format='png')
            buf.seek(0)
            charts['excavation_width_distribution'] = base64.b64encode(buf.getvalue()).decode('utf-8')
            buf.close()
            plt.close()
        except Exception as e:
            print("Error plotting excavation width distribution:", e)

        # Generate excavation height vs. excavation area scatter plot
        try:
            plt.figure(figsize=(8, 4))
            plt.scatter(data['excavation_height'], data['excavation_area'], alpha=0.6, color='green')
            plt.title('Excavation Height vs Excavation Area')
            plt.xlabel('Excavation Height')
            plt.ylabel('Excavation Area')
            buf = io.BytesIO()
            plt.savefig(buf, format='png')
            buf.seek(0)
            charts['excavation_height_vs_area'] = base64.b64encode(buf.getvalue()).decode('utf-8')
            buf.close()
            plt.close()
        except Exception as e:
            print("Error plotting excavation height vs. area scatter plot:", e)

        # Generate design section trend over time
        try:
            plt.figure(figsize=(8, 4))
            plt.plot(data['created_at'], data['design_section'], marker='o', color='purple')
            plt.title('Design Section Trend Over Time')
            plt.xlabel('Time')
            plt.ylabel('Design Section')
            buf = io.BytesIO()
            plt.savefig(buf, format='png')
            buf.seek(0)
            charts['design_section_trend'] = base64.b64encode(buf.getvalue()).decode('utf-8')
            buf.close()
            plt.close()
        except Exception as e:
            print("Error plotting design section trend:", e)

    # Render the template
    return render(
        request,
        'statics/geological_statistics.html',
        {
            'statistics': statistics,
            'charts': charts,
            'is_admin': is_admin,
        }
    )


@login_required
def excavation_calculation_statistics(request):
    """
    超欠挖计算统计视图：
    - 用户看到自己的数据统计。
    - 管理员看到全局数据统计。
    """
    # 判断是否是管理员
    is_admin = request.user.role == 'admin'

    # 获取数据集：管理员获取所有数据，普通用户获取个人数据
    if is_admin:
        records = OverUnderExcavationCalculation.objects.all()
    else:
        records = OverUnderExcavationCalculation.objects.filter(created_by=request.user)

    print("Records count:", records.count())  # 调试信息

    # 准备数据
    data = pd.DataFrame.from_records(
        records.values(
            'line_name',
            'north_direction_angle',
            'radius',
            'length',
            'height',
            'angle_increment',
            'created_at',
        )
    )

    # 初始化统计结果
    statistics = {}

    if not data.empty:
        # 基础统计
        statistics['record_count'] = len(data)
        statistics['radius_avg'] = data['radius'].mean()
        statistics['length_avg'] = data['length'].mean()
        statistics['height_avg'] = data['height'].mean()

        # 处理半径分布数据
        radius_stats = data.groupby('line_name')['radius'].agg(['mean', 'min', 'max']).reset_index()
        statistics['radius_data'] = {
            'labels': radius_stats['line_name'].tolist(),
            'means': radius_stats['mean'].tolist(),
            'mins': radius_stats['min'].tolist(),
            'maxs': radius_stats['max'].tolist()
        }

        # 处理长度和高度数据
        length_height_data = []
        for _, row in data.iterrows():
            length_height_data.append({
                'x': float(row['length']),
                'y': float(row['height'])
            })
        statistics['length_height_data'] = json.dumps(length_height_data)

        # 处理角度增量的时间序列数据
        data['year'] = pd.to_datetime(data['created_at']).dt.year
        angle_by_year = data.groupby('year')['angle_increment'].agg(['mean', 'min', 'max']).reset_index()

        # 确保包含2021-2025的所有年份数据
        all_years = range(2021, 2026)
        angle_data = []
        for year in all_years:
            year_stats = angle_by_year[angle_by_year['year'] == year]
            if not year_stats.empty:
                angle_data.append({
                    'mean': float(year_stats['mean'].iloc[0]),
                    'min': float(year_stats['min'].iloc[0]),
                    'max': float(year_stats['max'].iloc[0])
                })
            else:
                angle_data.append({
                    'mean': 0,
                    'min': 0,
                    'max': 0
                })

        statistics['angle_data'] = {
            'years': list(all_years),
            'means': [d['mean'] for d in angle_data],
            'mins': [d['min'] for d in angle_data],
            'maxs': [d['max'] for d in angle_data]
        }

        # 汇总统计数据
        statistics['summary_data'] = {
            'radius': statistics['radius_avg'],
            'length': statistics['length_avg'],
            'height': statistics['height_avg'],
            'angle': data['angle_increment'].mean()
        }

    print("Statistics data:", statistics)  # 调试信息

    return render(
        request,
        'statics/excavation_calculation_statistics.html',
        {
            'statistics': statistics,
            'is_admin': is_admin,
        }
    )

@login_required
def excavation_diagnosis_statistics(request):
    """
    超欠挖诊断统计视图：
    - 用户看到自己的数据统计。
    - 管理员看到全局数据统计。
    """
    # 判断是否是管理员
    is_admin = request.user.role == 'admin'

    # 获取数据集：管理员获取所有数据，普通用户获取个人数据
    if is_admin:
        records = ExcavationDiagnosis.objects.all()
    else:
        records = ExcavationDiagnosis.objects.filter(created_by=request.user)

    print("Records count:", records.count())  # 调试信息

    # 准备数据
    data = pd.DataFrame.from_records(
        records.values(
            'over_excavation_area',
            'under_excavation_area',
            'max_over_excavation',
            'max_under_excavation',
            'average_over_excavation',
            'average_under_excavation',
            'created_at',
        )
    )

    # 初始化统计结果
    statistics = {}

    if not data.empty:
        # 基础统计
        statistics['over_excavation_area_avg'] = data['over_excavation_area'].mean()
        statistics['under_excavation_area_avg'] = data['under_excavation_area'].mean()
        statistics['max_over_excavation'] = data['max_over_excavation'].max()
        statistics['max_under_excavation'] = data['max_under_excavation'].min()  # 使用min因为欠挖是负值

        # 处理散点图数据
        scatter_data = []
        for _, row in data.iterrows():
            scatter_data.append({
                'x': float(row['over_excavation_area']),
                'y': float(row['under_excavation_area'])
            })
        statistics['scatter_data'] = json.dumps(scatter_data)

        # 处理月度数据
        data['month'] = pd.to_datetime(data['created_at']).dt.strftime('%Y-%m')
        monthly_group = data.groupby('month').agg({
            'over_excavation_area': 'sum',
            'under_excavation_area': 'sum',
            'max_over_excavation': 'max',
            'max_under_excavation': 'min'  # 使用min因为欠挖是负值
        }).reset_index()

        # 按时间排序
        monthly_group = monthly_group.sort_values('month')

        statistics['months'] = monthly_group['month'].tolist()
        statistics['monthly_data'] = {
            'over': monthly_group['over_excavation_area'].tolist(),
            'under': monthly_group['under_excavation_area'].tolist(),
            'max_over': monthly_group['max_over_excavation'].tolist(),
            'max_under': monthly_group['max_under_excavation'].tolist()
        }

        # 处理时间序列数据
        data['date'] = pd.to_datetime(data['created_at']).dt.strftime('%Y-%m-%d')
        time_series = data.groupby('date').agg({
            'over_excavation_area': 'sum',
            'under_excavation_area': 'sum'
        }).reset_index()

        # 计算累计变化趋势
        time_series['cumulative'] = (time_series['over_excavation_area'] +
                                     time_series['under_excavation_area']).cumsum()

        # 按时间排序
        time_series = time_series.sort_values('date')

        # 添加到统计结果
        statistics['time_series'] = {
            'dates': time_series['date'].tolist(),
            'over': time_series['over_excavation_area'].tolist(),
            'under': time_series['under_excavation_area'].tolist(),
            'cumulative': time_series['cumulative'].tolist()
        }

    print("Statistics data:", statistics)  # 调试信息

    return render(
        request,
        'statics/excavation_diagnosis_statistics.html',
        {
            'statistics': statistics,
            'is_admin': is_admin,
        }
    )

@login_required
def tunnel_statistics(request):
    """
    隧道轮廓统计视图：
    - 用户看到自己的数据统计。
    - 管理员看到全局数据统计。
    """

    # 判断是否是管理员
    is_admin = request.user.role == 'admin'

    # 获取数据集：管理员获取所有数据，普通用户获取个人数据
    if is_admin:
        records = TunnelContourInfo.objects.all()
    else:
        records = TunnelContourInfo.objects.filter(created_by=request.user)
    print(records)
    # 准备数据
    data = pd.DataFrame.from_records(
        records.values(
            'cr',
            'rcl',
            'vo',
            'c1',
            'c2',
            'c3',
            'created_at',  # 记录创建时间，用于趋势分析
        )
    )

    # 初始化统计结果和图表
    statistics = {}
    charts = {}

    if not data.empty:
        # 统计指标
        statistics['record_count'] = len(data)  # 上传记录数
        statistics['avg_cr'] = data['cr'].mean()  # 调整指数平均值
        statistics['avg_rcl'] = data['rcl'].mean()  # 粗糙度平均值
        statistics['avg_vo'] = data['vo'].mean()  # 纵向超挖变化平均值

        # 调整指数分布图
        plt.figure(figsize=(8, 4))
        data['cr'].plot(kind='hist', bins=10, color='blue', alpha=0.7, edgecolor='black')
        plt.title('Adjustment Index (CR) Distribution')
        plt.xlabel('CR')
        plt.ylabel('Frequency')
        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        charts['cr_distribution'] = base64.b64encode(buf.getvalue()).decode('utf-8')
        buf.close()
        plt.close()

        # RCL vs VO 散点图
        plt.figure(figsize=(8, 4))
        plt.scatter(data['rcl'], data['vo'], alpha=0.6, color='green')
        plt.title('RCL vs VO')
        plt.xlabel('RCL')
        plt.ylabel('VO')
        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        charts['rcl_vs_vo'] = base64.b64encode(buf.getvalue()).decode('utf-8')
        buf.close()
        plt.close()
    time_series = data.groupby(
        pd.Grouper(key='created_at', freq='Y')
    )['vo'].mean().to_dict()

    statistics['longitudinal_trend'] = list(time_series.values())
    # 渲染模板
    return render(
        request,
        'statics/tunnel_statistics.html',
        {
            'statistics': statistics,
            'charts': charts,
            'is_admin': is_admin,
        }
    )

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.db.models import Q

@login_required
def user_management(request):
    """
    用户管理视图。
    - 显示所有用户。
    - 支持按用户名或邮箱搜索用户。
    """
    User = get_user_model()
    search_query = request.GET.get('search', '')  # 获取搜索框的输入
    if search_query:
        users = User.objects.filter(
            Q(username__icontains=search_query) | Q(email__icontains=search_query)
        )  # 按用户名或邮箱搜索
    else:
        users = User.objects.all()  # 默认展示所有用户

    return render(request, 'user_management/user_management.html', {'users': users, 'search_query': search_query})


@login_required
def edit_user(request, user_id):
    """
    修改用户角色视图。
    - 审计员 (auditor) 角色不可修改。
    - 只能在管理员 (admin) 和普通用户 (user) 之间切换。
    """
    User = get_user_model()
    user = get_object_or_404(User, id=user_id)

    # 如果用户是审计员，显示不可修改提示
    if user.role == 'auditor':
        return render(request, 'user_management/edit_user.html', {'user': user, 'cannot_modify': True})

    if request.method == 'POST':
        role = request.POST.get('role')
        if role in ['admin', 'user']:  # 验证提交的角色是否有效
            user.role = role
            user.save()
            return redirect('user_management')
        else:
            return render(request, 'user_management/edit_user.html', {'user': user, 'error': '无效的角色选择'})

    return render(request, 'user_management/edit_user.html', {'user': user, 'cannot_modify': False})


@login_required
def add_user(request):
    """
    新增用户视图。
    - 支持选择所有角色 (admin, user, auditor)。
    """
    User = get_user_model()
    if request.method == 'POST':
        username = request.POST.get('username')
        email = request.POST.get('email')
        password = request.POST.get('password')
        role = request.POST.get('role')

        # 验证输入数据
        if username and email and password and role in ['admin', 'user', 'auditor']:
            User.objects.create_user(username=username, email=email, password=password, role=role)
            return redirect('user_management')
        else:
            return render(request, 'user_management/add_user.html', {'error': '请输入完整信息并选择有效的角色'})

    return render(request, 'user_management/add_user.html')


@login_required
def delete_user(request, user_id):
    """
    删除用户视图。
    """
    User = get_user_model()
    user = get_object_or_404(User, id=user_id)
    user.delete()
    return redirect('user_management')


from .models import User, OverUnderExcavationCalculation, ExcavationDiagnosis, TunnelContourInfo
from .forms import GeologicalSketchRecordForm, ExcavationDiagnosisForm, OverUnderExcavationForm, TunnelContourForm
from django.contrib import messages  # 用于发送消息
from .models import GeologicalSketchRecord
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
import logging
logger = logging.getLogger(__name__)
from django.shortcuts import render

#分页函数
def paginate_queryset(queryset, request, items_per_page, page_param='page'):
    paginator = Paginator(queryset, items_per_page)
    page = request.GET.get(page_param, 1)  # 获取分页参数
    try:
        page_obj = paginator.page(page)
    except PageNotAnInteger:
        page_obj = paginator.page(1)  # 默认第 1 页
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)  # 超出范围返回最后一页
    return page_obj


#下面这四个是拆开的用户个人中心（掌子面，超欠挖计算，诊断，隧道）
@login_required
def user_records_view(request):
    """
    展示掌子面记录，包括新增、修改、删除、未通过和有效数据，并添加搜索功能。
    """
    # 获取搜索条件
    search_query = request.GET.get('search', '')  # 获取搜索的掌子面编号

    # 每页显示条数
    items_per_page = 10  # 调整为生产环境合适的值

    # 使用搜索逻辑
    def get_searched_queryset(base_queryset):
        """
        根据搜索条件返回查询集
        """
        if search_query:
            base_queryset = base_queryset.filter(face_id__icontains=search_query)
        return base_queryset

    # 应用搜索和分页
    pending_new_page_obj = paginate_queryset(
        get_searched_queryset(
            GeologicalSketchRecord.objects.filter(
                created_by=request.user, status='pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_new_page'
    )
    pending_modified_page_obj = paginate_queryset(
        get_searched_queryset(
            GeologicalSketchRecord.objects.filter(
                created_by=request.user, status='modified_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_modified_page'
    )
    pending_deleted_page_obj = paginate_queryset(
        get_searched_queryset(
            GeologicalSketchRecord.objects.filter(
                created_by=request.user, status='deleted_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_deleted_page'
    )
    rejected_page_obj = paginate_queryset(
        get_searched_queryset(
            GeologicalSketchRecord.objects.filter(
                created_by=request.user, status='rejected'
            )
        ),
        request,
        items_per_page,
        page_param='rejected_page'
    )
    approved_page_obj = paginate_queryset(
        get_searched_queryset(
            GeologicalSketchRecord.objects.filter(
                created_by=request.user, status__in=['uploaded_approved', 'modified_approved']
            )
        ),
        request,
        items_per_page,
        page_param='approved_page'
    )

    # 保留搜索参数的查询参数
    query_params = request.GET.copy()
    query_params.pop('page', None)  # 移除分页参数，避免重复
    query_params_encoded = query_params.urlencode()  # 编码查询参数

    # 渲染模板
    return render(
        request,
        'personal_center/user_records.html',  # 模板文件
        {
            'pending_new_geological': pending_new_page_obj,
            'pending_modified_geological': pending_modified_page_obj,
            'pending_deleted_geological': pending_deleted_page_obj,
            'rejected_geological': rejected_page_obj,
            'approved_geological': approved_page_obj,
            'search_query': search_query,  # 搜索条件
            'query_params': query_params_encoded,  # 用于分页链接
        },
    )





@login_required
def excavation_calculation_records_view(request):
    """
    展示超欠挖计算记录，包括新增、修改、删除、未通过和有效数据，并添加搜索功能和分页功能。
    """
    # 获取搜索条件
    search_query = request.GET.get('search', '')  # 获取搜索的超欠挖计算编号

    # 每页显示条数
    items_per_page = 10  # 调整为生产环境合适的值

    # 使用搜索逻辑
    def get_searched_queryset(base_queryset):
        """
        根据搜索条件返回查询集
        """
        if search_query:
            base_queryset = base_queryset.filter(face_id__icontains=search_query)  # 替换字段为实际字段
        return base_queryset

    # 应用搜索和分页
    pending_new_page_obj = paginate_queryset(
        get_searched_queryset(
            OverUnderExcavationCalculation.objects.filter(
                created_by=request.user, status='pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_new_page'
    )
    pending_modified_page_obj = paginate_queryset(
        get_searched_queryset(
            OverUnderExcavationCalculation.objects.filter(
                created_by=request.user, status='modified_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_modified_page'
    )
    pending_deleted_page_obj = paginate_queryset(
        get_searched_queryset(
            OverUnderExcavationCalculation.objects.filter(
                created_by=request.user, status='deleted_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_deleted_page'
    )
    rejected_page_obj = paginate_queryset(
        get_searched_queryset(
            OverUnderExcavationCalculation.objects.filter(
                created_by=request.user, status='rejected'
            )
        ),
        request,
        items_per_page,
        page_param='rejected_page'
    )
    approved_page_obj = paginate_queryset(
        get_searched_queryset(
            OverUnderExcavationCalculation.objects.filter(
                created_by=request.user, status__in=['uploaded_approved', 'modified_approved']
            )
        ),
        request,
        items_per_page,
        page_param='approved_page'
    )

    # 保留搜索参数的查询参数
    query_params = request.GET.copy()
    query_params.pop('page', None)  # 移除分页参数，避免重复
    query_params_encoded = query_params.urlencode()  # 编码查询参数

    # 渲染模板
    return render(
        request,
        'personal_center/user_records1.html',  # 模板文件
        {
            'pending_new_excavation_calculation': pending_new_page_obj,
            'pending_modified_excavation_calculation': pending_modified_page_obj,
            'pending_deleted_excavation_calculation': pending_deleted_page_obj,
            'rejected_excavation_calculation': rejected_page_obj,
            'approved_excavation_calculation': approved_page_obj,
            'search_query': search_query,  # 搜索条件
            'query_params': query_params_encoded,  # 用于分页链接
        },
    )




@login_required
def excavation_diagnosis_records_view(request):
    """
    展示超欠挖诊断记录，包括新增、修改、删除、未通过和有效数据，并添加搜索功能和分页功能。
    """
    # 获取搜索条件
    search_query = request.GET.get('search', '')  # 获取搜索条件

    # 每页显示条数
    items_per_page = 10  # 可以根据需求调整条目数

    # 使用搜索逻辑
    def get_searched_queryset(base_queryset):
        """
        根据搜索条件返回查询集
        """
        if search_query:
            base_queryset = base_queryset.filter(face_id__icontains=search_query)  # 替换为实际搜索字段
        return base_queryset

    # 应用搜索和分页
    pending_new_page_obj = paginate_queryset(
        get_searched_queryset(
            ExcavationDiagnosis.objects.filter(
                created_by=request.user, status='pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_new_page'
    )
    pending_modified_page_obj = paginate_queryset(
        get_searched_queryset(
            ExcavationDiagnosis.objects.filter(
                created_by=request.user, status='modified_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_modified_page'
    )
    pending_deleted_page_obj = paginate_queryset(
        get_searched_queryset(
            ExcavationDiagnosis.objects.filter(
                created_by=request.user, status='deleted_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_deleted_page'
    )
    rejected_page_obj = paginate_queryset(
        get_searched_queryset(
            ExcavationDiagnosis.objects.filter(
                created_by=request.user, status='rejected'
            )
        ),
        request,
        items_per_page,
        page_param='rejected_page'
    )
    approved_page_obj = paginate_queryset(
        get_searched_queryset(
            ExcavationDiagnosis.objects.filter(
                created_by=request.user, status__in=['uploaded_approved', 'modified_approved']
            )
        ),
        request,
        items_per_page,
        page_param='approved_page'
    )

    # 保留搜索参数的查询参数
    query_params = request.GET.copy()
    query_params.pop('page', None)  # 移除分页参数，避免重复
    query_params_encoded = query_params.urlencode()  # 编码查询参数

    # 渲染模板
    return render(
        request,
        'personal_center/user_records2.html',  # 模板文件
        {
            'pending_new_excavation_diagnosis': pending_new_page_obj,
            'pending_modified_excavation_diagnosis': pending_modified_page_obj,
            'pending_deleted_excavation_diagnosis': pending_deleted_page_obj,
            'rejected_excavation_diagnosis': rejected_page_obj,
            'approved_excavation_diagnosis': approved_page_obj,
            'search_query': search_query,  # 搜索条件
            'query_params': query_params_encoded,  # 用于分页链接
        },
    )




@login_required
def tunnel_contour_records_view(request):
    """
    展示隧道轮廓记录，包括新增、修改、删除、未通过和有效数据，并添加搜索功能和分页功能。
    """
    # 获取搜索条件
    search_query = request.GET.get('search', '')  # 获取搜索条件

    # 每页显示条数
    items_per_page = 10  # 根据需求调整条目数

    # 使用搜索逻辑
    def get_searched_queryset(base_queryset):
        """
        根据搜索条件返回查询集
        """
        if search_query:
            base_queryset = base_queryset.filter(face_id__icontains=search_query)  # 替换为实际搜索字段
        return base_queryset

    # 应用搜索和分页
    pending_new_page_obj = paginate_queryset(
        get_searched_queryset(
            TunnelContourInfo.objects.filter(
                created_by=request.user, status='pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_new_page'
    )
    pending_modified_page_obj = paginate_queryset(
        get_searched_queryset(
            TunnelContourInfo.objects.filter(
                created_by=request.user, status='modified_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_modified_page'
    )
    pending_deleted_page_obj = paginate_queryset(
        get_searched_queryset(
            TunnelContourInfo.objects.filter(
                created_by=request.user, status='deleted_pending'
            )
        ),
        request,
        items_per_page,
        page_param='pending_deleted_page'
    )
    rejected_page_obj = paginate_queryset(
        get_searched_queryset(
            TunnelContourInfo.objects.filter(
                created_by=request.user, status='rejected'
            )
        ),
        request,
        items_per_page,
        page_param='rejected_page'
    )
    approved_page_obj = paginate_queryset(
        get_searched_queryset(
            TunnelContourInfo.objects.filter(
                created_by=request.user, status__in=['uploaded_approved', 'modified_approved']
            )
        ),
        request,
        items_per_page,
        page_param='approved_page'
    )

    # 保留搜索参数的查询参数
    query_params = request.GET.copy()
    query_params.pop('page', None)  # 移除分页参数，避免重复
    query_params_encoded = query_params.urlencode()  # 编码查询参数

    # 渲染模板
    return render(
        request,
        'personal_center/user_records3.html',  # 模板文件
        {
            'pending_new_tunnel': pending_new_page_obj,
            'pending_modified_tunnel': pending_modified_page_obj,
            'pending_deleted_tunnel': pending_deleted_page_obj,
            'rejected_tunnel': rejected_page_obj,
            'approved_tunnel': approved_page_obj,
            'search_query': search_query,  # 搜索条件
            'query_params': query_params_encoded,  # 用于分页链接
        },
    )

#下面四个是用户修改新增待审批的
@login_required
def edit_pending_record(request, record_id):
    """
    用户修改待审批的掌子面记录记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        form = GeologicalSketchRecordForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')
    else:
        form = GeologicalSketchRecordForm(instance=record)

    return render(request, 'personal_center/edit_pending_record.html', {'form': form, 'record': record})

@login_required
def edit_pending_excavation_diagnosis(request, record_id):
    """
    用户修改待审批的超欠挖诊断记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        form = ExcavationDiagnosisForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')  # 替换为用户记录列表的URL名称
    else:
        form = ExcavationDiagnosisForm(instance=record)

    return render(request, 'personal_center/edit_pending_diagnosis.html', {'form': form, 'record': record})

@login_required
def edit_pending_over_under_excavation(request, record_id):
    """
    用户修改待审批的超欠挖计算记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        form = OverUnderExcavationForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')  # 替换为用户记录列表的URL名称
    else:
        form = OverUnderExcavationForm(instance=record)

    return render(request, 'personal_center/edit_pending_excavation.html', {'form': form, 'record': record})

@login_required
def edit_pending_tunnel_contour(request, record_id):
    """
    用户修改待审批的隧道轮廓记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        form = TunnelContourForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')  # 替换为用户记录列表的URL名称
    else:
        form = TunnelContourForm(instance=record)

    return render(request, 'personal_center/edit_pending_tunnel_contour.html', {'form': form, 'record': record})


#下面四个是用户删除新增待审批的
@login_required
def delete_pending_record(request, record_id):
    """
    用户删除待审批的掌子面信息记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_pending.html', {'record': record})

@login_required
def delete_pending_excavation_diagnosis(request, record_id):
    """
    用户删除待审批的超欠挖诊断记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')  # 替换为用户记录列表的URL名称

    return render(request, 'personal_center/confirm_delete_pending_diagnosis.html', {'record': record})

@login_required
def delete_pending_over_under_excavation(request, record_id):
    """
    用户删除待审批的超欠挖计算记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')  # 替换为用户记录列表的URL名称

    return render(request, 'personal_center/confirm_delete_pending_excavation.html', {'record': record})

@login_required
def delete_pending_tunnel_contour(request, record_id):
    """
    用户删除待审批的隧道轮廓记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status='pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')  # 替换为用户记录列表的URL名称

    return render(request, 'personal_center/confirm_delete_pending_tunnel_contour.html', {'record': record})

#下面八个是用户申请修改，删除已审批信息的
#掌子面用户申请修改已审批记录
@login_required
def apply_edit_record(request, record_id):
    """
    用户申请修改已审批记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        form = GeologicalSketchRecordForm(request.POST, instance=record)  # 用表单绑定数据
        if form.is_valid():
            operation_reason = request.POST.get('operation_reason')  # 获取修改理由
            if operation_reason:
                record.operation_reason = operation_reason  # 保存修改理由
            record.status = 'modified_pending'  # 修改申请设置为待审批状态
            record.save()
            form.save()  # 保存表单中的修改
            return redirect('user_records_view')
        else:
            print("表单验证失败，错误信息如下：")
            print(form.errors)
    else:
        form = GeologicalSketchRecordForm(instance=record)  # 初始化表单，加载已有数据

    return render(request, 'personal_center/apply_edit_record.html', {'record': record})

#掌子面用户申请删除已审批记录
@login_required
def apply_delete_record(request, record_id):
    """
    用户申请删除已审批记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        operation_reason = request.POST.get('operation_reason')  # 获取删除理由
        print("理由如下：")
        print(operation_reason)
        if operation_reason:
            record.operation_reason = operation_reason  # 保存删除理由
        record.status = 'deleted_pending'  # 删除申请设置为待审批状态
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_application.html', {'record': record})

@login_required
def apply_edit_excavation_diagnosis(request, record_id):
    """
    用户申请修改已审批的超欠挖诊断记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        form = ExcavationDiagnosisForm(request.POST, instance=record)  # 用表单绑定数据
        if form.is_valid():
            operation_reason = request.POST.get('operation_reason')  # 获取修改理由
            if operation_reason:
                record.operation_reason = operation_reason  # 保存修改理由
            record.status = 'modified_pending'  # 修改申请设置为待审批状态
            record.save()
            form.save()  # 保存表单中的修改
            return redirect('user_records_view')
        else:
            print("表单验证失败，错误信息如下：")
            print(form.errors)
    else:
        form = ExcavationDiagnosisForm(instance=record)  # 初始化表单，加载已有数据

    return render(request, 'personal_center/apply_edit_excavation_diagnosis.html', {'record': record})


@login_required
def apply_delete_excavation_diagnosis(request, record_id):
    """
    用户申请删除已审批的超欠挖诊断记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        operation_reason = request.POST.get('operation_reason')  # 获取删除理由
        print("理由如下：")
        print(operation_reason)
        if operation_reason:
            record.operation_reason = operation_reason  # 保存删除理由
        record.status = 'deleted_pending'  # 删除申请设置为待审批状态
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_excavation_diagnosis.html', {'record': record})

@login_required
def apply_edit_over_under_excavation_calculation(request, record_id):
    """
    用户申请修改已审批的超欠挖计算记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        form = OverUnderExcavationForm(request.POST, instance=record)  # 用表单绑定数据
        if form.is_valid():
            operation_reason = request.POST.get('operation_reason')  # 获取修改理由
            if operation_reason:
                record.operation_reason = operation_reason  # 保存修改理由
            record.status = 'modified_pending'  # 修改申请设置为待审批状态
            record.save()
            form.save()  # 保存表单中的修改
            return redirect('user_records_view')
        else:
            print("表单验证失败，错误信息如下：")
            print(form.errors)
    else:
        form = OverUnderExcavationForm(instance=record)  # 初始化表单，加载已有数据

    return render(request, 'personal_center/apply_edit_over_under_excavation_calculation.html', {'record': record})


@login_required
def apply_delete_over_under_excavation_calculation(request, record_id):
    """
    用户申请删除已审批的超欠挖计算记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])
    if request.method == 'POST':
        operation_reason = request.POST.get('operation_reason')  # 获取删除理由
        print("理由如下：")
        print(operation_reason)
        if operation_reason:
            record.operation_reason = operation_reason  # 保存删除理由
        record.status = 'deleted_pending'  # 删除申请设置为待审批状态
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_over_under_excavation_calculation.html', {'record': record})

@login_required
def apply_edit_tunnel_contour_info(request, record_id):
    """
    用户申请修改已审批的隧道轮廓记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        form = TunnelContourForm(request.POST, instance=record)  # 用表单绑定数据
        if form.is_valid():
            operation_reason = request.POST.get('operation_reason')  # 获取修改理由
            if operation_reason:
                record.operation_reason = operation_reason  # 保存修改理由
            record.status = 'modified_pending'  # 修改申请设置为待审批状态
            record.save()
            form.save()  # 保存表单中的修改
            return redirect('user_records_view')
        else:
            print("表单验证失败，错误信息如下：")
            print(form.errors)
    else:
        form = TunnelContourForm(instance=record)  # 初始化表单，加载已有数据

    return render(request, 'personal_center/apply_edit_tunnel_contour_info.html', {'record': record})


@login_required
def apply_delete_tunnel_contour_info(request, record_id):
    """
    用户申请删除已审批的隧道轮廓信息记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status__in=['uploaded_approved', 'modified_approved'])

    if request.method == 'POST':
        operation_reason = request.POST.get('operation_reason')  # 获取删除理由
        print("理由如下：")
        print(operation_reason)
        if operation_reason:
            record.operation_reason = operation_reason  # 保存删除理由
        record.status = 'deleted_pending'  # 删除申请设置为待审批状态
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_tunnel_contour_info.html', {'record': record})

#下面四个是修改“修改待审批”的
@login_required
def edit_modified_pending_record(request, record_id):
    """
    用户修改掌子面“修改待审批”的记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        form = GeologicalSketchRecordForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')
    else:
        form = GeologicalSketchRecordForm(instance=record)

    return render(request, 'personal_center/edit_pending_record.html', {'form': form, 'record': record})

@login_required
def edit_modified_pending_excavation_diagnosis(request, record_id):
    """
    用户修改超欠挖诊断“修改待审批”的记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        form = ExcavationDiagnosisForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')
    else:
        form = ExcavationDiagnosisForm(instance=record)

    return render(request, 'personal_center/edit_pending_diagnosis.html', {'form': form, 'record': record})

@login_required
def edit_modified_pending_over_under_excavation(request, record_id):
    """
    用户修改超欠挖计算“修改待审批”的记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        form = OverUnderExcavationForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')
    else:
        form = OverUnderExcavationForm(instance=record)

    return render(request, 'personal_center/edit_pending_excavation.html', {'form': form, 'record': record})

@login_required
def edit_modified_pending_tunnel_contour(request, record_id):
    """
    用户修改隧道轮廓“修改待审批”的记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        form = TunnelContourForm(request.POST, instance=record)
        if form.is_valid():
            form.save()
            return redirect('user_records_view')
    else:
        form = TunnelContourForm(instance=record)

    return render(request, 'personal_center/edit_pending_tunnel_contour.html', {'form': form, 'record': record})


#下面四个是删除“修改待审批”的
@login_required
def delete_modified_pending_record(request, record_id):
    """
    用户删除掌子面“修改待审批”的记录。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_pending.html', {'record': record})

@login_required
def delete_modified_pending_excavation_diagnosis(request, record_id):
    """
    用户删除超欠挖诊断“修改待审批”的记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_pending_diagnosis.html', {'record': record})

@login_required
def delete_modified_pending_over_under_excavation(request, record_id):
    """
    用户删除超欠挖计算“修改待审批”的记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_pending_excavation.html', {'record': record})

@login_required
def delete_modified_pending_tunnel_contour(request, record_id):
    """
    用户删除隧道轮廓“修改待审批”的记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status='modified_pending')

    if request.method == 'POST':
        record.delete()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_pending_tunnel_contour.html', {'record': record})


#下面四个是取消“删除待审批”的
@login_required
def delete_deleted_pending_record(request, record_id):
    """
    用户取消“删除待审批”的记录（即撤销删除申请，将状态改为上传已审批）。
    """
    record = get_object_or_404(GeologicalSketchRecord, id=record_id, created_by=request.user, status='deleted_pending')

    if request.method == 'POST':
        # 将状态从 deleted_pending 改为 uploaded_approved
        record.status = 'uploaded_approved'
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/confirm_delete_deleted_pending.html', {'record': record})

@login_required
def cancel_delete_pending_excavation_diagnosis(request, record_id):
    """
    用户取消“删除待审批”的超欠挖诊断记录。
    """
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user, status='deleted_pending')

    if request.method == 'POST':
        record.status = 'uploaded_approved'
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/cancel_delete_pending_excavation_diagnosis.html', {'record': record})

@login_required
def cancel_delete_pending_over_under_excavation(request, record_id):
    """
    用户取消“删除待审批”的超欠挖计算记录。
    """
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='deleted_pending')

    if request.method == 'POST':
        record.status = 'uploaded_approved'
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/cancel_delete_pending_over_under_excavation.html', {'record': record})

@login_required
def cancel_delete_pending_tunnel_contour(request, record_id):
    """
    用户取消“删除待审批”的隧道轮廓记录。
    """
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user, status='deleted_pending')

    if request.method == 'POST':
        record.status = 'uploaded_approved'
        record.save()
        return redirect('user_records_view')

    return render(request, 'personal_center/cancel_delete_pending_tunnel_contour.html', {'record': record})


#下面四个是重新申请的
@login_required
def reapply_record(request, record_id):
    """
    用户重新申请审批未通过的掌子面记录。
    """
    # 获取未通过的记录
    record = get_object_or_404(
        GeologicalSketchRecord, id=record_id, created_by=request.user, status='rejected'
    )

    if request.method == 'POST':
        # 将用户提交的数据绑定到表单
        form = GeologicalSketchRecordForm(request.POST, instance=record)
        if form.is_valid():
            # 更新记录状态为 pending，表示重新申请
            record = form.save(commit=False)
            record.status = 'pending'  # 重新进入待审批状态
            record.operation_reason = request.POST.get('operation_reason')  # 更新申请理由
            record.save()

            # 提示用户操作成功
            messages.success(request, f"记录 {record.face_id} 已成功重新申请审批。")
            return redirect('user_records_view')  # 重定向到用户记录页面
        else:
            # 打印表单错误，方便调试
            print("Form Errors:", form.errors)
            return render(request, 'personal_center/reapply_record.html', {'form': form, 'record': record, 'errors': form.errors})
    else:
        # GET 请求，绑定已有数据到表单
        form = GeologicalSketchRecordForm(instance=record)
    return render(request, 'personal_center/reapply_record.html', {'form': form, 'record': record})

@login_required
def reapply_excavation_diagnosis(request, record_id):
    """
    用户重新申请审批未通过的超欠挖诊断记录。
    """
    # 获取未通过的记录
    record = get_object_or_404(
        ExcavationDiagnosis, id=record_id, created_by=request.user, status='rejected'
    )

    if request.method == 'POST':
        # 将用户提交的数据绑定到表单
        form = ExcavationDiagnosisForm(request.POST, instance=record)
        if form.is_valid():
            # 更新记录状态为 pending，表示重新申请
            record = form.save(commit=False)
            record.status = 'pending'  # 重新进入待审批状态
            record.operation_reason = request.POST.get('operation_reason')  # 更新申请理由
            record.save()

            # 提示用户操作成功
            messages.success(request, f"记录 {record.face_id} 已成功重新申请审批。")
            return redirect('user_records_view')  # 重定向到用户记录页面
        else:
            # 打印表单错误，方便调试
            print("Form Errors:", form.errors)
            return render(request, 'personal_center/reapply_excavation_diagnosis.html', {'form': form, 'record': record, 'errors': form.errors})
    else:
        # GET 请求，绑定已有数据到表单
        form = ExcavationDiagnosisForm(instance=record)
    return render(request, 'personal_center/reapply_excavation_diagnosis.html', {'form': form, 'record': record})

@login_required
def reapply_over_under_calculation(request, record_id):
    """
    用户重新申请审批未通过的超欠挖计算记录。
    """
    # 获取未通过的记录
    record = get_object_or_404(
        OverUnderExcavationCalculation, id=record_id, created_by=request.user, status='rejected'
    )

    if request.method == 'POST':
        # 将用户提交的数据绑定到表单
        form = OverUnderExcavationForm(request.POST, instance=record)
        if form.is_valid():
            # 更新记录状态为 pending，表示重新申请
            record = form.save(commit=False)
            record.status = 'pending'  # 重新进入待审批状态
            record.operation_reason = request.POST.get('operation_reason')  # 更新申请理由
            record.save()

            # 提示用户操作成功
            messages.success(request, f"记录 {record.face_id} 已成功重新申请审批。")
            return redirect('user_records_view')  # 重定向到用户记录页面
        else:
            # 打印表单错误，方便调试
            print("Form Errors:", form.errors)
            return render(request, 'personal_center/reapply_over_under_calculation.html', {'form': form, 'record': record, 'errors': form.errors})
    else:
        # GET 请求，绑定已有数据到表单
        form = OverUnderExcavationForm(instance=record)
    return render(request, 'personal_center/reapply_over_under_calculation.html', {'form': form, 'record': record})

@login_required
def reapply_tunnel_contour(request, record_id):
    """
    用户重新申请审批未通过的隧道轮廓信息记录。
    """
    # 获取未通过的记录
    record = get_object_or_404(
        TunnelContourInfo, id=record_id, created_by=request.user, status='rejected'
    )

    if request.method == 'POST':
        # 将用户提交的数据绑定到表单
        form = TunnelContourForm(request.POST, instance=record)
        if form.is_valid():
            # 更新记录状态为 pending，表示重新申请
            record = form.save(commit=False)
            record.status = 'pending'  # 重新进入待审批状态
            record.operation_reason = request.POST.get('operation_reason')  # 更新申请理由
            record.save()

            # 提示用户操作成功
            messages.success(request, f"记录 {record.face_id} 已成功重新申请审批。")
            return redirect('user_records_view')  # 重定向到用户记录页面
        else:
            # 打印表单错误，方便调试
            print("Form Errors:", form.errors)
            return render(request, 'personal_center/reapply_tunnel_contour.html', {'form': form, 'record': record, 'errors': form.errors})
    else:
        # GET 请求，绑定已有数据到表单
        form = TunnelContourForm(instance=record)
    return render(request, 'personal_center/reapply_tunnel_contour.html', {'form': form, 'record': record})


#下面四个是用户查看信息的
@login_required
def view_GeologicalSketchRecord(request, record_id):
    """
    用户查看待审批的掌子面记录记录（只读）。
    """
    # 获取指定记录
    record = get_object_or_404(GeologicalSketchRecord, id=record_id)
    # 初始化表单实例
    form = GeologicalSketchRecordForm(instance=record)
    # 将所有字段设置为只读
    for field_name, field in form.fields.items():
        field.widget.attrs['readonly'] = 'readonly'
        field.widget.attrs['class'] = 'form-control'
    # 渲染模板并传递表单和记录
    return render(request, 'personal_center/view_GeologicalSketchRecord.html', {'form': form, 'record': record})

@login_required
def view_ExcavationDiagnosis(request, record_id):
    """
    用户查看超欠挖诊断记录（只读）。
    """
    # 获取记录
    record = get_object_or_404(ExcavationDiagnosis, id=record_id, created_by=request.user)
    # 初始化表单实例
    form = ExcavationDiagnosisForm(instance=record)
    # 设置所有字段为只读
    for field_name, field in form.fields.items():
        field.widget.attrs['readonly'] = 'readonly'
        field.widget.attrs['class'] = 'form-control'
    # 渲染模板
    return render(request, 'personal_center/view_ExcavationDiagnosis.html', {'form': form, 'record': record})

@login_required
def view_OverUnderExcavation(request, record_id):
    """
    用户查看超欠挖计算记录（只读）。
    """
    # 获取记录
    record = get_object_or_404(OverUnderExcavationCalculation, id=record_id, created_by=request.user)
    # 初始化表单实例
    form = OverUnderExcavationForm(instance=record)
    # 设置所有字段为只读
    for field_name, field in form.fields.items():
        field.widget.attrs['readonly'] = 'readonly'
        field.widget.attrs['class'] = 'form-control'
    # 渲染模板
    return render(request, 'personal_center/view_OverUnderExcavation.html', {'form': form, 'record': record})


@login_required
def view_TunnelContour(request, record_id):
    """
    用户查看隧道轮廓记录（只读）。
    """
    # 获取记录
    record = get_object_or_404(TunnelContourInfo, id=record_id, created_by=request.user)
    # 初始化表单实例
    form = TunnelContourForm(instance=record)
    # 设置所有字段为只读
    for field_name, field in form.fields.items():
        field.widget.attrs['readonly'] = 'readonly'
        field.widget.attrs['class'] = 'form-control'
    # 渲染模板
    return render(request, 'personal_center/view_TunnelContour.html', {'form': form, 'record': record})

